/**
 * 
 */
package com.cn.practice.command;

/**
 * 命令模式 命令模式核心是将命令定义为一个对象 通过命令的重新编排实现发起者和接收者的解耦 原理 将军-命令-士兵(命令聚合了士兵动作组成命令
 * 将军只需要发布命令就能调度士兵) 
 * 命令模式所有的命令都要有撤销命令的操作
 * 命令模式最好都设置空命令操作，这样方便非空判断
 * @author zhangyang
 *
 */
public class CommandPractice {
	public static void main(String[] args) {
		/**
		 * 使用命令模式-实现一个遥控器控制多个电器操作
		 */
		CommandSender commandSender = new CommandSender();
		commandSender.addCommand(0, new TVOnCommand(new TV()), new TVOffCommand(new TV()));
		commandSender.on(0);
		commandSender.off(0);
		//撤销操作
		commandSender.cancelCommand();
		/**
		 * 如果添加DVD控制 那么很简单 加上DVD的接受类和DVD命令 然后发送者聚合一下就OK了
		 */
		commandSender.addCommand(1, new DVDOnCommand(new DVD()), new DVDOffCommand(new DVD()));
		commandSender.on(1);
		commandSender.off(1);
		/**
		 * jdbcTemplate就是采用的命令模式设计的
		 * query方法QueryStatementCallback 这就是一个查询命令的返回值做内部类实现
		 */
	}
}

/**
 * 具体接受人
 * 
 * @author zhangyang
 *
 */
class TV {
	public void on() {
		System.out.println("电视打开了");
	}

	public void off() {
		System.out.println("电视关闭了");
	}
}

class DVD {
	public void dvdOn() {
		System.out.println("DVD打开了");
	}

	public void dvdOff() {
		System.out.println("DVD关闭了");
	}
}

/**
 * 命令接口
 * 
 * @author zhangyang
 *
 */
interface Command {
	// 执行命令
	void doCommand();

	// 撤销命令
	void undo();
}

/**
 * 电视打开命令
 * 
 * @author zhangyang
 *
 */
class TVOnCommand implements Command {

	TV tv;

	public TVOnCommand(TV tv) {
		super();
		this.tv = tv;
	}

	@Override
	public void doCommand() {
		// TODO Auto-generated method stub
		tv.on();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		tv.off();
	}

}

/**
 * 电视关闭命令
 * @author zhangyang
 *
 */
class TVOffCommand implements Command{

	TV tv;

	public TVOffCommand(TV tv) {
		super();
		this.tv = tv;
	}

	@Override
	public void doCommand() {
		// TODO Auto-generated method stub
		tv.off();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		tv.on();
	}
}

/**
 * DVD打开命令
 * 
 * @author zhangyang
 *
 */
class DVDOnCommand implements Command {

	DVD tv;

	public DVDOnCommand(DVD tv) {
		super();
		this.tv = tv;
	}

	@Override
	public void doCommand() {
		// TODO Auto-generated method stub
		tv.dvdOn();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		tv.dvdOff();
	}

}

/**
 * DVD关闭命令
 * @author zhangyang
 *
 */
class DVDOffCommand implements Command{

	DVD dvd;

	public DVDOffCommand(DVD dvd) {
		super();
		this.dvd = dvd;
	}

	@Override
	public void doCommand() {
		// TODO Auto-generated method stub
		dvd.dvdOff();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		dvd.dvdOn();
	}
}

/**
 * 空命令-无操作
 * @author zhangyang
 *
 */
class NullCommand implements Command{


	@Override
	public void doCommand() {
		// TODO Auto-generated method stub
		
	}


	@Override
	public void undo() {
		// TODO Auto-generated method stub
		
	}
	
}

/**
 * 命令发送方
 * @author zhangyang
 *
 */
class CommandSender{
	//打开命令集合
	Command[] onCommand=new Command[5];
	//关闭命令集合
	 Command[] offCommand=new Command[5];
	//撤销命令 用以保存上次执行的命令
	Command cancel;
	//初始化
	public CommandSender() {
		super();
		cancel=new NullCommand();
		for(int i=0;i<4;i++) {
			onCommand[i]=new NullCommand();
			offCommand[i]=new NullCommand();
		}
	}
	
	/**
	 * 添加一个命令到指定命令组下标里
	 * @param index
	 * @param on
	 * @param off
	 */
	public void addCommand(int index,Command on,Command off) {
		onCommand[index]=on;
		offCommand[index]=off;
	}
	
	//打开命令-只需要输入下标就能打开对应的电器
	public void on(int index) {
		onCommand[index].doCommand();
		cancel=onCommand[index];
	}
	//关闭命令-只需要输入下标就能关闭对应的电器
	public void off(int index) {
		offCommand[index].doCommand();
		cancel=offCommand[index];
	}
	
	//撤销操作-记录上次命令 调用上次命令相反的操作就可以了
	public void cancelCommand() {
		this.cancel.undo();
	}
}